package dk.itu.samd.group2.tagapastry.messages;

import java.io.IOException;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Hashtable;
import java.util.Set;

import rice.environment.logging.Logger;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.NodeHandle;
import rice.p2p.commonapi.rawserialization.InputBuffer;
import rice.p2p.commonapi.rawserialization.OutputBuffer;
import dk.itu.samd.group2.tagapastry.Index;
import dk.itu.samd.group2.tagapastry.IndexEntry;
import dk.itu.samd.group2.tagapastry.IndexImpl;
import dk.itu.samd.group2.tagapastry.NodeState;
import dk.itu.samd.group2.tagapastry.Tag;
import dk.itu.samd.group2.tagapastry.TagapastryImpl;

public class ReplicationMsg extends TagapastryMsg {
	
	public static final int TYPE = 11;
	private static final long serialVersionUID = TYPE;
	//private Hashtable<Tag, Index> indexes;
	private NodeState ns;
	
	/*
	 * We need this constructor, as this message will be passed between
	 * nodes from the leaf set.
	 */
	public ReplicationMsg(Tag tag, NodeHandle sender, NodeHandle recipient, 
			NodeState state) {
		
		super(tag,sender,recipient);
		this.ns = state;
	}
	
	/*
	 * We need this constructor, as this message will be passed between
	 * nodes from the leaf set.
	 */
	public ReplicationMsg(InputBuffer in, Endpoint endpoint) throws IOException {
		super(in,endpoint);
		this.ns = new NodeState(in, endpoint);
	}
	
	public synchronized int getSize() {
		//System.out.println("Super size : " + super.getSize());
		//System.out.println("Ns size is: " + ns.getSize());
		return super.getSize() + ns.getSize();
	}
	
	/*
	 * (non-Javadoc)
	 * @see dk.itu.samd.group2.tagapastry.messages.TagapastryMsg#execute(dk.itu.samd.group2.tagapastry.Tagapastry)
	 */
	public void execute(TagapastryImpl impl) {
		// What should happen with that message once it arrives to a node		
		if(impl.getLoger().level <= Logger.FINEST)
			impl.getLoger().log("Delivered from: " + this.getSenderId() + " to " + this.getRecepientId());
				
		
		// Small output
//		System.out.println("Adding new data to: " + recepientHandle);
//		for(Index i : this.ns.getIndexes().values()) {
//			for (IndexEntry ie : i.getEntries()) {
//				System.out.println(ie);
//			}
//		}
		
		Hashtable<NodeHandle, NodeState> leafData = impl.getLeafSetData();
		if(leafData == null)
			leafData = new Hashtable<NodeHandle, NodeState>();
	
		if(this.ns.getIndexes().size() > 0) { 		
			if(leafData.containsKey(this.originalSender)) {
				//  Refresh content
				leafData.remove(this.originalSender);
			}
			
			leafData.put(this.originalSender, this.ns);

			// In worst case we can add indexes, but perhaps next call will be able to do it.
			int index = 0;
			do {
				try {
					Collection<Index> set = ns.getIndexes().values();
					Index[] indexes = (Index[])set.toArray(new Index[set.size()]);
				
					for(int i=0; i < indexes.length; i++) {
						if(impl.getIndexes() == null) {
							impl.putIndex(indexes[i]);
						}
						else if(!impl.getIndexes().values().contains(indexes[i])) {
							impl.putIndex(indexes[i]);
						}
					}
					return;
				}catch(ConcurrentModificationException cce) {
					System.out.println("Puha in replication msg");
					index++;
				}
			}while(index < 10);
		} else {
			// How do I remove non-existing indexes mhm... ?
			// Get those indexes if any from the leaf set and remove one-by-one
			NodeState toRemove = leafData.get(this.originalSender);
			if(toRemove == null || toRemove.getIndexes() == null) {
				return;
			}
			
			for(Tag t : toRemove.getIndexes().keySet()) {
				if(impl.getIndexes().containsKey(t)) {
					System.out.println("Removing " + t + " from leaf set.");
					impl.getIndexes().remove(t);
				}
			}
		}
	
	}
	
	/*
	 * (non-Javadoc)
	 * @see dk.itu.samd.group2.tagapastry.messages.TagapastryMsg#serialize(rice.p2p.commonapi.rawserialization.OutputBuffer)
	 */
	public void serialize(OutputBuffer buf) throws IOException {
		super.serialize(buf);
		ns.serialize(buf);
	}
	
	/*
	 * (non-Javadoc)
	 * @see dk.itu.samd.group2.tagapastry.messages.TagapastryMsg#toString()
	 */
	public String toString() {
		return "Replication Msg"; // tmp
	}
	
	/*
	 * (non-Javadoc)
	 * @see rice.p2p.commonapi.rawserialization.RawMessage#getType()
	 */
	public short getType() {
		return TYPE;
	}


	/*
	 * (non-Javadoc)
	 * @see dk.itu.samd.group2.tagapastry.messages.TagapastryMsg#getRecepientId()
	 */	
	public Id getRecepientId() {
		return this.recepientHandle.getId();
	}
}
